from hashlib import new
from time import sleep
from flask import jsonify
from numpy import append, integer
from sqlalchemy import null
from sqlalchemy import and_
import datetime
# from app.api import app
import app.api
# import memory_class
from app.api.memory_class import ChargingPileState, AllCarInformation, CancellableTimer
from app.models import UserInformation, ChargingDetail, ChargingPileReport
from app import db, mailbox
from app.api import get_value, set_value
from app.api.func import ask_for_line_number, id_to_name, name_to_id, change_request_charging_quantity, change_pattern, user_begin_charging, user_stop_charging, call_next_to_charge, call_next_from_waiting, cancel_charging, pile_fault_priority, pile_fault_time, pile_restore, find_line_num, judge_detail, get_true_line_num,cost_of_charging

# user

# 获取当前充电状态，返回状态码


def get_current_state(user_name):
    user_id = name_to_id(user_name)
    F_num = 0
    T_num = 0
    for i in get_value("car_information"):
        if i.get_state() == 3:
            if i.get_pattern() == "F":
                F_num += 1
            elif i.get_pattern() == "T":
                T_num += 1
    if judge_detail(user_name) == 0:  # 没有下订单的用户
        ans = {"state": 0, "num": 0, "pile_id": 0, "F": F_num,
               "T": T_num, "charge_quantity": 0, "charge_mode": "", "total_quantity": 0, "remaining_time": 0}
        return ans

    for i in get_value("car_information"):
        if (i.get_user_id() == user_id and i.get_state() != 4 and i.get_state() != 6):
            state = i.get_state()
            charge_mode = i.get_pattern()
            num = i.get_line_number()
            pile_id = i.get_pile()
            charge_quantity = i.get_request_charging_quantity()
            total_quantity = i.get_total_battery_capacity()
            if (charge_mode == "F"):
                F_num_new = find_line_num(user_name, "F")
                T_num_new = T_num
            else:
                T_num_new = find_line_num(user_name, "T")
                F_num_new = F_num
            remaining_time = 0
            if ((state == 5 or state == 1) and get_value(user_name) != KeyError):
                remaining_time = get_value(
                    user_name).interval-(datetime.datetime.now()-get_value(user_name).get_start_time()).seconds
            ans = {"state": state, "num": num, "pile_id": pile_id, "F": F_num_new,
                   "T": T_num_new, "charge_quantity": charge_quantity, "charge_mode": charge_mode, "total_quantity": total_quantity, "remaining_time": remaining_time}
            return ans


# 获取当前F、T排队人数，返回json {F: xx, T: xx}


def get_queue_size(user_name):
    user_id = name_to_id(user_name)
    F_num = 0
    T_num = 0
    for i in get_value("car_information"):
        if i.get_state() == 3:
            if i.get_pattern() == "F":
                F_num += 1
            elif i.get_pattern() == "T":
                T_num += 1
    if judge_detail(user_name) == 0:  # 没有下订单的用户
        ans = {"F": F_num, "T": T_num}
        return ans

    for i in get_value("car_information"):
        if i.get_user_id() == user_id:
            charge_mode = i.get_pattern()
            if (charge_mode == "F"):
                F_num_new = find_line_num(user_name, "F")
                T_num_new = T_num
            else:
                T_num_new = find_line_num(user_name, "T")
                F_num_new = F_num
            ans = {"F": F_num_new, "T": T_num_new}
            return ans

# 叫号，返回号码（直接返回对应charge_type的数字或Txx的格式都行)


def call_number(user_name, charge_type, charge_capacity, total_capacity):
    # user_name = id_to_name(user_id)
    [line_number, pile_id, state] = [0, 0, 0]
    [line_number, pile_id, state] = ask_for_line_number(
        user_name, total_capacity, charge_capacity, charge_type)
    num = 1
    re = get_queue_size(user_name)
    F_num = re["F"]
    T_num = re["T"]
    if (line_number == -1):  # 没有位置
        state = 0
    # 这里可能就需要发通知了####################
    # 如果用户是直接去的充电位置############
    # 这里不需要通知,根据返回值判断
    # 同时也要开始计时
    if state == 5:  # 准备充电的
        call_and_start_time(user_name, pile_id)

    return {"state": state, "num": line_number, "pile_id": pile_id, "F": F_num,
            "T": T_num}

# 修改充电量


def change_charging_capacity(user_name, capacity):
    # user_name = id_to_name(user_id)
    ans = change_request_charging_quantity(user_name, capacity)
    return bool(ans)

# 修改充电方式，返回号码


def change_charging_type(user_name, charge_type):
    # user_name = id_to_name(user_id)
    [line_number, pile_id, state] = [0, 0, 0]
    [line_number, pile_id, state] = change_pattern(user_name, charge_type)
    num = 1
    re = get_queue_size(user_name)
    F_num = re["F"]
    T_num = re["T"]
    if (line_number <= 0):  # 没有位置
        state = 0
    # 这里可能就需要发通知了####################
    # 如果用户是直接去的充电位置############
    # 这里不需要通知,根据返回值判断
    # 同时也要开始计时
    if state == 5:
        call_and_start_time(user_name, pile_id)

    return {"state": state, "num": line_number, "pile_id": pile_id, "F": F_num,
            "T": T_num}

# 开始充电


def start_charge(user_name):
    # user_name = id_to_name(user_id)

    # 停止等候计时
    get_value(user_name).cancel()

    charging_duration = user_begin_charging(user_name)

    # 开始进行充电计时
    job = CancellableTimer(interval=charging_duration,
                           function=stop_charge, user_name=user_name, type="system")
    job.start_OL()
    set_value(user_name, job)


# 停止充电，返回本次详单


def stop_charge(user_name, type):
    # from app.api import app
    with app.api.app.app_context():
        user_id = name_to_id(user_name)
        [charging_quantity, charging_cost,
            pile_id] = user_stop_charging(user_name, type)

        # 停止计时行为
        get_value(user_name).cancel()

        # 然后拿到这次的详单
        detail = ChargingDetail.query.filter(and_(
            ChargingDetail.user_id == user_id, ChargingDetail.complete == "yes")).all()[-1]
        re_detail = {"bill_id": detail.bill_id, "bill_time": detail.bill_time,
                     "charging_pile_id": detail.charging_pile_id, "charging_quantity": detail.charging_quantity, "charging_duration": detail.charging_duration, "starting_time": detail.starting_time, "stopping_time": detail.stopping_time, "charging_cost": detail.charging_cost, "service_cost": detail.service_cost, "total_cost": detail.total_cost, "user_id": user_id}
        # 删除这个车在car_information的信息
        car_information_list = get_value("car_information")
        for i in car_information_list:
            if (i.get_user_id() == user_id and i.get_state() == 4):
                car_information_list.remove(i)
                break

        # 接下来我们要叫下一辆车
        # pile_id = int(detail.charging_pile_id)
        now_charge_id = user_id
        # 弹出第一个元素
        pile_all = get_value("pile_car")
        pile_str = "pile"+str(pile_id)
        del(pile_all[pile_str][0])
        # 获得下一个准备充电的车辆
        next_charge_id = 0
        next_charge_id = call_next_to_charge(pile_id)  # 同时修改为准备充电
        # 这里需要发通知了####################
        # 用户是直接去的充电位置############
        if next_charge_id != 0:
            next_user_name = id_to_name(next_charge_id)
            line_number = get_true_line_num(next_user_name)
            mailbox[next_user_name].queue.put(
                {'state': 5, 'num': line_number, 'pile_id': pile_id, 'message': "call to charging position"})
        # 同时也要开始计时
            call_and_start_time(next_user_name, pile_id)

        # 获得下一个进入的车辆
        next_in_id = 0
        next_in_id = call_next_from_waiting(pile_id)
        # print(datetime.datetime.now(),"  ","next_in_id:",next_in_id)
        if next_in_id != 0:
            # 发送通知
            next_in_name = id_to_name(next_in_id)
            line_number = get_true_line_num(next_in_name)
            mailbox[next_in_name].queue.put(
                {'state': 2, 'num': line_number, 'pile_id': pile_id, 'message': "call to charging area waiting"})
            # 修改充电桩的信息
            pile_all = get_value("pile_car")
            pile_str = "pile"+str(pile_id)
            pile_all[pile_str].append(next_in_id)
            set_value("free_space", get_value("free_space")+1)

        #如果是system自动停止,这里我们就要给用户发通知,告知充电结束#######################
        if (type == "system"):
            mailbox[user_name].queue.put(
                {'state': 4, 'num': 0, 'pile_id': pile_id, 'message': "charging is over"})
        # return re_detail
        # return (re_detail)
        if (charging_quantity != 0):
            return True
        else:
            return False

# 获取所有详单，返回json，按详单格式


def get_orders(user_name):
    user_id = name_to_id(user_name)
    detail_all = ChargingDetail.query.filter(and_(
        ChargingDetail.user_id == user_id, ChargingDetail.complete == "yes")).all()
    ans = []
    for detail in detail_all:

        seconds_duration = detail.charging_duration
        # days = seconds_duration//(60*60*24)
        # seconds_duration = seconds_duration % (60*60*24)
        # hours = seconds_duration//(60*60)
        # seconds_duration = seconds_duration % (60*60)
        # minutes = seconds_duration//60
        # seconds = seconds_duration % 60
        # duration = str(hours)+"小时" + \
        #     str(minutes)+"分钟"+str(seconds)+"秒"
        duration = str(round(seconds_duration/60, 2))+"分钟"

        re_detail = {"bill_id": detail.bill_id, "bill_time": (detail.bill_time).strftime('%Y-%m-%d %H:%M:%S'),
                     "charging_pile_id": detail.charging_pile_id, "charging_quantity": round(detail.charging_quantity, 2), "charging_duration": duration, "starting_time": (detail.starting_time).strftime('%Y-%m-%d %H:%M:%S'), "stopping_time": (detail.stopping_time).strftime('%Y-%m-%d %H:%M:%S'), "charging_cost": round(detail.charging_cost, 2), "service_cost": round(detail.service_cost, 2), "total_cost": round(detail.total_cost, 2), "user_id": user_id}
        ans.append(re_detail)
    return ans


def cancel_charge(user_name):

    # user_name = id_to_name(user_id)
    car_information_list = get_value("car_information")
    # 检查状态,停止计时
    for i in car_information_list:
        if (id_to_name(i.get_user_id()) == user_name and (i.get_state() == 5 or i.get_state() == 1)):
            get_value(user_name).cancel()

    [next_charge_id, next_in_id] = cancel_charging(user_name)
    # 删除这个车在car_information的信息
    for i in car_information_list:
        if (id_to_name(i.get_user_id()) == user_name and i.get_state() == 6):
            pile_id = i.get_pile()
            car_information_list.remove(i)
            break
    # 这里需要发通知了####################
    # 用户是直接去的充电位置############
    if (next_charge_id != 0):
        next_charge_name = id_to_name(next_charge_id)
        line_number = get_true_line_num(next_charge_name)
        mailbox[next_charge_name].queue.put(
            {'state': 5, 'num': line_number, 'pile_id': pile_id, 'message': "call to charging position"})
    if (next_in_id != 0):
        next_in_name = id_to_name(next_in_id)
        line_number = get_true_line_num(next_in_name)
        mailbox[next_in_name].queue.put(
            {'state': 2, 'num': line_number, 'pile_id': pile_id, 'message': "call to charging area waiting"})

    # 同时也要开始计时
    if (next_charge_id != 0):
        next_user_name = id_to_name(next_charge_id)
        call_and_start_time(next_user_name, pile_id)

# admin
# 获取充电桩状态


def get_pile_states():
    charging_pile_state_list = get_value("charging_pile_state")
    ans = {}
    j = 0
    for i in charging_pile_state_list:
        j += 1
        temp = []
        if i.get_proper_functioning() == 1:
            temp.append("ON")
        else:
            temp.append("OFF")

        times = "充电次数"+str(i.get_cumulative_charging_times())+"次"
        temp.append(times)

        seconds_duration = i.get_cumulative_charging_duration()
        days = seconds_duration//(60*60*24)
        seconds_duration = seconds_duration % (60*60*24)
        hours = seconds_duration//(60*60)
        seconds_duration = seconds_duration % (60*60)
        minutes = seconds_duration//60
        seconds = seconds_duration % 60
        duration = "充电时长"+str(days)+"天"+str(hours)+"小时" + \
            str(minutes)+"分钟"+str(seconds)+"秒"
        temp.append(duration)

        quantity = "充电电量" + \
            str(round(i.get_accumulative_charging_quantity(), 1))+"度"
        temp.append(quantity)
        ans[str(j)] = temp
    return ans


# 获取等候车辆信息
def get_cars_message():
    car_information_list = get_value("car_information")
    ans = {"1": [], "2": [], "3": [], "4": [], "5": []}
    len = get_value("charging_space")
    pile = [0, 0, 0, 0, 0, 0]

    for i in car_information_list:
        if(i.get_state() == 2):
            temp = []
            total_battery_capacity = str(i.get_total_battery_capacity())+"度"

            request_charging_quantity = str(
                i.get_request_charging_quantity())+"度"
            queue_time = (datetime.datetime.now() -
                          i.get_start_queue_time()).seconds
            temp = [id_to_name(i.get_user_id()),
                    total_battery_capacity, request_charging_quantity, queue_time]
            ans[str(i.get_pile())].append(temp)
            # 记录一下哪些得到了
            pile[i.get_pile()] += 1
    # 填补没有的
    for i in range(1, 6):
        while(pile[i] <= 1):
            temp = [" ", " ", " ", " "]
            ans[str(i)].append(temp)
            pile[i] += 1
    # return dict(sorted(ans.items()))
    return ans


# 获取充电桩报表
def get_pile_reports():
    pile_report_all = ChargingPileReport.query.all()
    ans = {}
    for i in pile_report_all:
        temp = []
        id = i.charging_pile_id
        time = i.showing_time.strftime('%Y-%m-%d %H:%M')
        times = str(i.cumulative_charging_times)+"次"

        seconds_duration = round(i.cumulative_charging_duration, 0)
        days = seconds_duration//(60*60*24)
        seconds_duration = seconds_duration % (60*60*24)
        hours = seconds_duration//(60*60)
        seconds_duration = seconds_duration % (60*60)
        minutes = seconds_duration//60
        seconds = seconds_duration % 60
        duration = str(days)+"天"+str(hours)+"小时" + \
            str(minutes)+"分钟"+str(seconds)+"秒"

        quantity = str(round(i.accumulative_charging_quantity, 1))+"度"
        charging_cost = str(round(i.accumulative_charging_cost, 1))+"元"
        service_cost = str(round(i.accumulative_service_cost, 1))+"元"
        total_cost = str(round(i.accumulative_total_cost, 1))+"元"

        temp = [time, times, duration, quantity,
                charging_cost, service_cost, total_cost]

        ans[id] = temp
    return ans


def off_pile(pile_id):
    for i in get_value("charging_pile_state"):  # 关闭
        if i.get_charging_pile_id() == pile_id:
            i.change_functioning(0)
            break
    if (pile_id == 1 or pile_id == 2):
        pattern = "F"
    else:
        pattern = "T"
    car_information_list = get_value("car_information")
    for i in car_information_list:
        if(i.get_pile() == pile_id and (i.get_state() == 5 or i.get_state() == 1)):
            get_value(id_to_name(i.get_user_id())).cancel()
    strategy = get_value("strategy")
    if strategy == "priority":
        re_ans = pile_fault_priority(pile_id)
        # 发通知################
        for i in re_ans:
            #[new_user_name, new_pile_id, new_state]
            if (i[2] == 5 and i[0] != ""):  # state=5
                line_number = get_true_line_num(i[0])
                call_and_start_time(i[0], i[1])
                mailbox[i[0]].queue.put(
                    {'state': 5, 'num': line_number, 'pile_id': i[1], 'message': "call to charging position"})
            elif (i[2] == 2 and i[0] != ""):
                mailbox[i[0]].queue.put(
                    {'state': 2, 'num': line_number, 'pile_id': i[1], 'message': "call to charging area waiting"})
            elif (i[2] == 3 and i[0] != ""):
                # num = get_true_line_num(i[0])
                mailbox[i[0]].queue.put(
                    {'state': 3, 'num': line_number, 'pile_id': 0, 'message': "call to waiting area"})

    elif strategy == "time":
        re_ans = pile_fault_time(pile_id)
        # 发通知################
        for i in re_ans:
            #[new_user_name, new_pile_id, new_state]
            if (i[2] == 5 and i[0] != ""):  # state=5
                line_number = get_true_line_num(i[0])
                call_and_start_time(i[0], i[1])
                mailbox[i[0]].queue.put(
                    {'state': 5, 'num': line_number, 'pile_id': i[1], 'message': "call to charging position"})
            elif (i[2] == 2 and i[0] != ""):
                mailbox[i[0]].queue.put(
                    {'state': 2, 'num': line_number, 'pile_id': i[1], 'message': "call to charging area waiting"})
            elif (i[2] == 3 and i[0] != ""):
                # num = get_true_line_num(i[0])
                mailbox[i[0]].queue.put(
                    {'state': 3, 'num': line_number, 'pile_id': 0, 'message': "call to waiting area"})


def open_pile(pile_id):
    for i in get_value("charging_pile_state"):  # 关闭
        if i.get_charging_pile_id() == pile_id:
            i.change_functioning(1)
    re_ans = pile_restore(pile_id)
    if (pile_id == 1 or pile_id == 2):
        pattern = "F"
    else:
        pattern = "T"
    # 发通知################
    for i in re_ans:
        #[new_user_name, new_pile_id, new_state]
        if (i[2] == 5 and i[0] != ""):  # state=5
            line_number = get_true_line_num(i[0])
            call_and_start_time(i[0], i[1])
            mailbox[i[0]].queue.put(
                {'state': 5, 'num': line_number, 'pile_id': i[1], 'message': "call to charging position"})
        elif (i[2] == 2 and i[0] != ""):
            mailbox[i[0]].queue.put(
                {'state': 2, 'num': line_number, 'pile_id': i[1], 'message': "call to charging area waiting"})
        elif (i[2] == 3 and i[0] != ""):
            # num = get_true_line_num(i[0])
            mailbox[i[0]].queue.put(
                {'state': 3, 'num': line_number, 'pile_id': 0, 'message': "call to waiting area"})
# 通知用户超时,订单被取消


def time_out(user_name):
    # from app.api import app
    with app.api.app.app_context():
        # 首先确定用户目前状态是准备充电
        car_information_list = get_value("car_information")
        for i in car_information_list:
            if (id_to_name(i.get_user_id()) == user_name and i.get_state() == 5):

                #通知#####################
                mailbox[user_name].queue.put(
                    {'state': 6, 'num': 0, 'pile_id': 0, 'message': "time out,cancel the charging request"})
                print(datetime.datetime.now(), "  ", user_name, "time out")
                cancel_charge(user_name)
                # print(datetime.datetime.now(),"  ","next charge id", next_charge_id)

# 发通知告知用户前往充电桩的充电位置充电,并开始计时


def call_and_start_time(user_name, pile_id):
    job = CancellableTimer(interval=get_value(
        "waiting_time"), function=time_out, user_name=user_name)
    job.start_OL()
    set_value(user_name, job)


def translation(state, num, pile_id):
    mess = ""
    if (state == 2):
        mess = "Please go to the "+pile_id + \
            " pile and waiting for a moment, you will be the next one to charge!"
    elif (state == 3):
        mess = "Please go to the waiting area and be patient, your line number is "+num+"!"
    elif (state == 4):
        mess = "Congratulations！ Your charge request has been completed, welcome to come again!"
    elif (state == 5):
        mess = "Please go to the "+pile_id+" pile, ready to begin your charging!"
    elif(state == 6):
        mess = "Time out, you haven't begun your charging for a long time, cause automatic cancellation of charging!"
    return mess


# 输出所有充电桩队列的车辆信息以及等候区车辆信息,格式参照验收
# (V1,0.83,1.00),(V13,F,110)-(V14,F,95)-(V16,F,60)
def show_all_information():
    pile_all = get_value("pile_car")
    print(pile_all)
    car_information_list = get_value("car_information")
    for pile_id in range(1, 6):
        if (pile_id == 1 or pile_id == 2):
            pattern = "F"
            speed = get_value("F_speed")
        else:
            pattern = "T"
            speed = get_value("T_speed")
        pile_str = "pile"+str(pile_id)
        print(pile_str, ":", end=" ")
        pile_car_list = pile_all[pile_str]
        for user_id in pile_car_list:
            # 我们找到这个人
            try:
                old_detail = ChargingDetail.query.filter(and_(ChargingDetail.charging_pile_id == str(
                    pile_id), ChargingDetail.user_id == user_id, ChargingDetail.complete == "no"))
                starting_time = old_detail.first().starting_time
                stopping_time = datetime.datetime.now()
                charging_duration = (stopping_time-starting_time).seconds
                charging_quantity = charging_duration*speed/3600
                charging_cost = cost_of_charging(
                    starting_time, stopping_time, pattern)
                service_cost = charging_quantity * \
                    get_value("unit_service_cost")
                ans = "("+"V"+str(user_id)+","+str(round(charging_quantity, 2)
                                                   )+","+str(round(charging_cost+service_cost, 2))+")"
                print(ans, end=" ")
            except:
                ans = "("+"V"+str(user_id)+","+str(round(0, 2)
                                                   )+","+str(round(0, 2))+")"
                print(ans, end=" ")
        print()
    # 接下来看等候区的车
    # print("waiting area", ":", end=" ")
    show_ans = ""
    for i in car_information_list:
        if (i.get_state() == 3):
            temp_ans = "("+"V"+str(i.get_user_id())+"," + \
                i.get_pattern()+"," + \
                str(round(i.get_request_charging_quantity(), 2))+")"
            show_ans = show_ans + temp_ans + "-"
    print("waiting area", ":", show_ans)
